﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Threading.Tasks;
using System.Data.SqlClient;

namespace VectorSelection
{
    public class kMeansVectorSelection
    {
        public string InputFileName;
        public string OutputFileName = @"..\..\log.txt";
        public string[] HeadersofColums;
        public bool CheckedHeaders = false;
        public List<Vector> VectorList;   //All of the Vectors
        private List<Vector> TrnVectors;   //Vectors in Training Set
        private List<Vector> TestVectors;   //Vectors in Test Set

        public void ChangeProgress(int progress)
        {

        }

        public void ChangeStatus(string txt)
        {

        }

        public int SizeofVector
        {
            get
            {
                try
                {
                    return VectorList[0].Body.Count;
                }
                catch (Exception)
                {
                    return 0;
                }
            }
            set { }
        }
        /// <summary>
        /// Reads Vectors from a .txt file. For proper work, there must not be a headline and the Y value should be the last attribute (column);
        /// </summary>
        /// <param name="InputFileName"> Full path to the .txt file</param>
        /// <param name="HowMany"> Specifies the amount of lines which will be read from file. Do not specifie if you want to read the  whole file.</param>
        public void GetVectorsFromtxt(string InputFileName, int HowMany = 0)
        {
            this.InputFileName = InputFileName;
            this.VectorList = new List<Vector>();
            System.Globalization.CultureInfo ci = new System.Globalization.CultureInfo("en-us");
            System.Threading.Thread.CurrentThread.CurrentCulture = ci;

            StreamReader sr = new StreamReader(InputFileName);
            string s1;

            if (HowMany == 0)
                while ((s1 = sr.ReadLine()) != null)
                {
                    string[] s2 = s1.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                    if (HeadersofColums == null && CheckedHeaders)
                        HeadersofColums = s2;

                    else
                    {
                        Vector V1 = new Vector();
                        for (int i = 0; i < s2.Length; i++)
                        {
                            if (i == (s2.Length - 1)) V1.YValue = Convert.ToDouble(s2[i]);
                            else
                                V1.Body.Add(i, Convert.ToDouble(s2[i]));
                        }
                        VectorList.Add(V1);
                    }
                }
            else
                while (((s1 = sr.ReadLine()) != null) && (VectorList.Count <= HowMany))
                {
                    string[] s2 = s1.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                    if (HeadersofColums == null && CheckedHeaders)
                        HeadersofColums = s2;

                    else
                    {
                        Vector V1 = new Vector();
                        for (int i = 0; i < s2.Length; i++)
                        {
                            if (i == (s2.Length - 1)) V1.YValue = Convert.ToDouble(s2[i]);
                            else
                                V1.Body.Add(i, Convert.ToDouble(s2[i]));
                        }
                        VectorList.Add(V1);
                    }
                }
            sr.Close();
        }


        /// <summary>
        /// Writes Vectors to a specified .txt file. 
        /// </summary>
        /// <param name="Output"> Full path to the .txt file</param>
        public void ExportVectorsToTxt(string Output)
        {
            StreamWriter SW = new StreamWriter(Output);
            for (int i = 0; i < VectorList.Count; i++)
            {
                for (int j = 0; j < VectorList[i].Body.Count; j++)
                {
                    SW.Write(VectorList[i].Body[j] + "\t");
                }
                SW.Write(VectorList[i].YValue);
                SW.WriteLine();
            }
            SW.Close();
        }

        /// <summary>
        /// Distributes Vectors to Training set and to Test set. Takes more time, but distribution is better.
        /// </summary>
        private void DistributeVectors()    //Uses a kMeans method for better distribution of Vectors
        {
            //I noticed, that a normal random method is too unstable on those data-sets I've tested (irisflower, steel_carbon and steel_temperature)
            //so i decided to use a kMeans method to make a great amount of clusters, so now i can distribute Vectors more stable by adding 
            //Vectors from every part of n-dimensional space. This guarantees that there is no chance of not-including in Trn set any of Vectors 
            //from a specified part of space, so there could be a mess in further computing. For example- if there is a group of near Vectors in
            //some part of space and all of them are in Test Set, there can be a problem with calculating their YValues.
            //I guess.
            //I'm not sure.
            //But seems to work properly.
            TrnVectors = new List<Vector>();   //lista wektorów zbioru treningowego
            TestVectors = new List<Vector>();   //lista wektorów zbioru testowego

            kMeans k1 = new kMeans();
            int K = (int)(VectorList.Count * 0.125);
            k1.DokMeans(K, VectorList);
            List<List<Vector>> Temp = new List<List<Vector>>();

            for (int i = 0; i < K; i++)
                if (k1.HowManyInGroupX(i) > 0)
                    Temp.Add(k1.ReturnVectorsFromCluster(i));


            for (int i = 0; i < (0.75 * VectorList.Count - 1); i++)
            {
                TrnVectors.Add(Temp[i % Temp.Count][0]);
                Temp[i % Temp.Count].RemoveAt(0);
                if (Temp[i % Temp.Count].Count == 0)
                    Temp.RemoveAt(i % Temp.Count);
            }

            for (int i = 0; i < Temp.Count; i++)
            {
                foreach (Vector V in Temp[i])
                {
                    TestVectors.Add(V);
                }
            }

            Console.WriteLine("W zbiorze treningowym: " + TrnVectors.Count);
            Console.WriteLine("W zbiorze testowym: " + TestVectors.Count);
        }

        /// <summary>
        /// Distributes Vectors to Training set and to Test set. Lighter version of DistributeVectors() method. This method is faster, but don't guarantee proper distribution of Vectors.
        /// </summary>
        private void SlimDistributeVectors()    //This method uses a Random to distribute Vectors to TrnVectors and TestVectors lists.
        {
            TrnVectors = new List<Vector>();
            TestVectors = new List<Vector>();
            for (int i = 0; i < VectorList.Count; i++)  //First thing- copying all of the Vectors to TestVectors List
                TestVectors.Add(VectorList[i]);

            while (TrnVectors.Count < (0.75 * VectorList.Count - 1)) //75% of Vectors go to Training Set and 25% will be testing my calculation
            {
                Random rnd = new Random();
                int k = rnd.Next(TestVectors.Count);    // getting a random iterator
                TrnVectors.Add(TestVectors[k]);     //Adding [k] Vector to Training data set...
                TestVectors.RemoveAt(k);            //... and removing it from current(test) set
            }
            Console.WriteLine("W zbiorze treningowym: " + TrnVectors.Count);
            Console.WriteLine("W zbiorze testowym: " + TestVectors.Count);
        }

        private static double EuclideanDistance(Vector v1, Vector v2)
        {
            double temp = 0.0;

            for (int i = 0; i < v1.Body.Count; i++)
            {
                temp += Math.Pow(v2.Body[i] - v1.Body[i], 2);
            }

            return Math.Sqrt(temp);
        }

        private static double ManhattanDistance(Vector v1, Vector v2)
        {
            double temp = 0.0;

            for (int i = 0; i < v1.Body.Count; i++)
            {
                temp += Math.Abs(v1.Body[i] - v2.Body[i]);
            }

            return temp;
        }

        private static double ChebyshevDistance(Vector v1, Vector v2)
        {
            double temp = 0.0;

            for (int i = 0; i < v1.Body.Count; i++)
            {

                if (Math.Abs(v1.Body[i] - v2.Body[i]) > temp)
                {
                    temp = Math.Abs(v1.Body[i] - v2.Body[i]);
                }

            }

            return temp;
        }

        private static double MinkowskiDistance(Vector v1, Vector v2, decimal p)
        {
            double temp = 0.0;



            for (int i = 0; i < v1.Body.Count; i++)
            {
                temp += Math.Pow(Math.Abs(v2.Body[i] - v1.Body[i]), Convert.ToDouble(p));
            }

            return Math.Pow(temp, 1.00 / Convert.ToDouble(p));
        }
        public class Vector //Vector class by Tomasz Rzepka
        {

            public Dictionary<int, double> Body = new Dictionary<int, double>();
            public string Id { get; set; }
            public double YValue { get; set; }
            public double PredictionError { get; set; }     //dodałem zmienną przedstawiającą ewentualny błąd przewidywań dla danego wektora

            public Vector(char delimiter, string line, bool ColumnAsY)
            {
                string[] tab = line.Split(delimiter);
                int x = 0;

                for (int i = 0; i < tab.Count() - 1; i++)
                {

                    if (i == 0 && ColumnAsY == true)
                    {
                        this.YValue = Convert.ToDouble(tab[i]);

                    }
                    else
                    {
                        this.Body.Add(x, Convert.ToDouble(tab[i]));
                        x++;
                    }

                }

                this.Id = tab[tab.Count() - 1];
            }

            public Vector() { }
        }
        public class kMeans //kMeans class by Tomasz Rzepka
        {
            public int[,] ClusteredSet { get; set; }
            public bool cancelled = false;
            public StringBuilder Result { get; set; }
            public List<Vector> Centroids { get; set; } //Centers of centroids
            private List<Vector> Set { get; set; }
            public kMeans()
            {

            }


            public void DokMeans(int k, List<Vector> set, string metric = "euclidean", decimal minkowski_p = 0.1m)
            {
                Set = new List<Vector>();
                Set = set;
                cancelled = false;
                Result = new StringBuilder();
                Result.Clear();

                int iter = 0;

                ClusteredSet = new int[k, set.Count];
                Centroids = new List<Vector>();
                Double[,] DistanceMatrix = new Double[k, set.Count];
                int[,] G = new int[k, set.Count];
                int[,] iK2 = new int[k, set.Count];

                //losowanie poczatkowych srodkow centroidow

                int n = set.Count;
                int l = 0;
                Random rnd = new Random();
                HashSet<int> used = new HashSet<int>();

                //creating first set
                for (int i = n - k; i < n; i++)
                {
                    int off = rnd.Next(i + 1);
                    if (!used.Add(off))
                    {
                        off = i;
                        used.Add(off);

                    }
                    Centroids.Add(set[off]);
                    l++;
                }

                do
                {
                    iter++;

                    for (int i = 0; i < G.GetLength(0); i++)
                    {
                        for (int j = 0; j < G.GetLength(1); j++)
                        {
                            iK2[i, j] = G[i, j];
                        }
                    }

                    //obliczanie odleglosci obiektow od centrow
                    //StreamWriter SW = new StreamWriter(@"..\..\dist.txt");
                    for (int i = 0; i < k; i++)
                    {

                        for (int j = 0; j < set.Count; j++)
                        {

                            if (metric == "euclidean")
                            {
                                DistanceMatrix[i, j] = EuclideanDistance(Centroids[i], set[j]);
                                //if (DistanceMatrix[i, j] == Double.PositiveInfinity || DistanceMatrix[i, j] == Double.NegativeInfinity)
                                //{
                                //    SW.Write(Centroids[i].Body[0] + "\t" + Centroids[i].Body[1] + "\t\t" + set[j].Body[0] + "\t" + set[j].Body[1]);
                                //    SW.WriteLine();
                                //}
                            }

                            if (metric == "chebyshev")
                            {
                                DistanceMatrix[i, j] = ChebyshevDistance(Centroids[i], set[j]);
                            }

                            if (metric == "minkowski")
                            {
                                DistanceMatrix[i, j] = MinkowskiDistance(Centroids[i], set[j], minkowski_p);
                            }

                            if (metric == "manhattan")
                            {
                                DistanceMatrix[i, j] = ManhattanDistance(Centroids[i], set[j]);
                            }
                        }
                    }
                    //SW.Close();
                    //przydzielenie obiektów do klastrów

                    int[,] IndexTable = new int[set.Count, 1];
                    for (int i = 0; i < set.Count; i++)
                    {
                        double min = DistanceMatrix[0, i];
                        for (int j = 0; j < k; j++)
                        {
                            double x = DistanceMatrix[j, i];
                            if (DistanceMatrix[j, i] <= min)
                            {
                                min = DistanceMatrix[j, i];
                                IndexTable[i, 0] = j;

                            }

                        }
                    }



                    for (int i = 0; i < k; i++)
                    {
                        for (int j = 0; j < set.Count; j++)
                        {
                            if (i == IndexTable[j, 0])
                            {
                                G[i, j] = 1;
                            }
                            else
                            {
                                G[i, j] = 0;
                            }
                        }
                    }

                    Centroids.Clear();

                    for (int i = 0; i < k; i++)
                    {
                        List<Vector> VectorVarTempList = new List<Vector>(); // temporary list to create Vector which shows center of cluster
                        for (int j = 0; j < set.Count; j++)
                        {
                            if (G[i, j] == 1)
                            {
                                VectorVarTempList.Add(set[j]);
                            }
                        }

                        if (VectorVarTempList.Count == 0)
                        {
                            Vector BaseVector = new Vector();
                            int i2 = 0;
                            foreach (var item in set[0].Body)
                            {
                                BaseVector.Body.Add(i2, Double.PositiveInfinity);
                                i2++;
                            }
                            VectorVarTempList.Add(BaseVector);
                        }

                        Vector test = CalculateNewCentroids(VectorVarTempList);
                        Centroids.Add(test);
                        VectorVarTempList.Clear();

                    }
                } while (CompareTables(G, iK2) == false && cancelled == false);

                this.ClusteredSet = G;

                Result.AppendLine("kMeans RESULTS:");
                Result.AppendLine("metric: " + metric);
                Result.AppendLine("iteration count: " + iter);
                Result.AppendLine("---------------------");

                int y = 0;
                foreach (var item in HowManyInGroup())
                {
                    Result.AppendLine("Cluster no. " + y + "- " + item + " items");
                    y++;
                }


                //calculation of centroids centers Y value

                int w = 0;

                foreach (var item in Centroids)
                {
                    double sumaY = 0.0;
                    //liczenie wartosci Y dla klastra item

                    if (item.Body[0] != Double.PositiveInfinity)
                    {

                        foreach (var item2 in ReturnVectorsFromCluster(w))
                        {
                            sumaY += item2.YValue;
                        }
                        sumaY /= HowManyInGroupX(w);

                        Centroids[w].YValue = sumaY;
                        Centroids[w].Id = Convert.ToString(this.Set.Count + w);
                    }



                    w++;

                }


            }

            public void ExportClustersToFile(List<Vector> VectorSet, string Path, bool WithValues)
            {


                //Writing centres of clusters to file

                StreamWriter ClustersCenterWriter = new StreamWriter(string.Format(@"{0}\centroids_centers_kMeans.txt", Path), false);

                for (int i = 0; i < this.Centroids.Count; i++)
                {
                    ClustersCenterWriter.Write(this.Centroids[i].YValue);
                    for (int j = 0; j < this.Centroids[i].Body.Count; j++)
                    {
                        ClustersCenterWriter.Write("    " + this.Centroids[i].Body[j]);
                    }

                    if (i < this.Centroids.Count - 1)
                    {
                        ClustersCenterWriter.WriteLine("    " + this.Centroids[i].Id);
                    }
                    else
                    {
                        ClustersCenterWriter.Write("    " + this.Centroids[i].Id);
                    }
                }

                ClustersCenterWriter.Close();
                StreamWriter[] ClusterFileTable = new StreamWriter[ClusteredSet.GetLength(0)];
                int[] counters = new int[ClusteredSet.GetLength(0)];
                int[] HMIEG = HowManyInGroup();
                //creating blank files
                for (int i = 0; i < ClusteredSet.GetLength(0); i++)
                {
                    StreamWriter sw = new StreamWriter(string.Format(@"{0}\{1}_kMeans.txt", Path, i), false);
                    ClusterFileTable[i] = sw;
                }

                //putting data into files
                for (int i = 0; i < ClusteredSet.GetLength(0); i++)
                {

                    counters[i] = 0;
                    for (int j = 0; j < ClusteredSet.GetLength(1); j++)
                    {

                        if (ClusteredSet[i, j] == 1)
                        {
                            counters[i]++;
                            StringBuilder sb = new StringBuilder();
                            sb.Clear();
                            //sb.Append(VectorSet[j].Body[0] + "\t");
                            if (WithValues == true)
                            {
                                sb.Append(VectorSet[j].YValue + "\t");
                            }
                            for (int k = 0; k < VectorSet[j].Body.Count; k++)
                            {

                                sb.Append(Convert.ToString(VectorSet[j].Body[k] + "\t"));

                            }
                            sb.Append(VectorSet[j].Id);
                            if (counters[i] != HMIEG[i])
                            {
                                ClusterFileTable[i].WriteLine(sb.ToString());
                            }
                            else
                            {
                                ClusterFileTable[i].Write(sb.ToString());
                            }

                        }
                    }
                }

                //closing files

                foreach (var item in ClusterFileTable)
                {
                    item.Close();

                }



            }


            public int[] HowManyInGroup()
            {
                int[,] group = this.ClusteredSet;
                int[] table = new int[group.GetLength(0)];

                for (int i = 0; i < group.GetLength(0); i++)
                {
                    int temp = 0;
                    for (int j = 0; j < group.GetLength(1); j++)
                    {
                        temp += group[i, j];
                    }

                    table[i] = temp;
                }
                return table;
            }

            public int HowManyInGroupX(int i)
            {
                int temp = 0;
                for (int j = 0; j < this.ClusteredSet.GetLength(1); j++)
                {
                    temp += this.ClusteredSet[i, j];
                }

                return temp;
            }

            public List<Vector> ReturnVectorsFromCluster(int i)
            {
                List<Vector> Result = new List<Vector>();
                for (int j = 0; j < this.ClusteredSet.GetLength(1); j++)
                {
                    if (this.ClusteredSet[i, j] == 1)
                    {
                        Result.Add(Set[j]);
                    }
                }

                return Result;
            }
            private bool CompareTables(int[,] t1, int[,] t2)
            {
                //returns true if tables contains the same values
                bool info = true;
                for (int i = 0; i < t1.GetLength(0); i++)
                {
                    for (int j = 0; j < t1.GetLength(1); j++)
                    {
                        if (t1[i, j] != t2[i, j])
                        {
                            info = false;
                        }
                    }
                }
                return info;
            }

            private Vector CalculateNewCentroids(List<Vector> VectorList)
            {
                Vector w1 = new Vector();

                if (VectorList.Count != 0)
                {


                    double[,] tab = new double[VectorList.Count, VectorList[0].Body.Count];
                    for (int i = 0; i < tab.GetLength(0); i++)
                    {
                        for (int j = 0; j < tab.GetLength(1); j++)
                        {
                            tab[i, j] = VectorList[i].Body[j];
                        }
                    }

                    for (int i = 0; i < tab.GetLength(1); i++)
                    {
                        double sum = 0.00;
                        for (int j = 0; j < tab.GetLength(0); j++)
                        {
                            sum += tab[j, i];
                        }

                        w1.Body.Add(i, sum / VectorList.Count);
                    }
                }

                return w1;
            }

        }


        /// <summary>
        /// Returns a reduced List&#60;Vector&#62;, which is approximately as representative, as the input List&#60;Vector&#62;
        /// </summary>
        /// <param name="List&#60;Vector&#62;"> VectorList</param>
        public void SelectVectors()//public List<Vector> SelectVectors(List<Vector> VectorList)
        {
            StreamWriter SW = new StreamWriter(OutputFileName);
            kMeans k1 = new kMeans();
            //DistributeVectors();
            /*for (int i = 0; i < 100; i++)
            {
                DistributeVectors();
                Console.WriteLine();
                Parallel.Invoke(
                    () =>
                    {
                        KnnAlgorithm(1);
                        KnnAlgorithm(3);
                        KnnAlgorithm(5);
                    },
                        () =>
                        {
                            KnnAlgorithm(7);
                            KnnAlgorithm(11);
                        }
                );
            }*/

            for (int iii = 300; iii <= VectorList.Count * 0.3; /*iii+=20*/) //Loop for calculating for a different numbers of clusters in kMeans method
            {
                DateTime PreLoopTime = DateTime.Now;
                /**/
                DistributeVectors();
                k1.DokMeans(iii, VectorList);
                List<Vector> CentersOfCentroids = new List<Vector>();
                for (int i = 0; i < iii; i++)
                    if (k1.HowManyInGroupX(i) > 0) CentersOfCentroids.Add(k1.Centroids[i]);


                Parallel.Invoke     //I'm calculating in the same time for 2 different "k" in knn method
                (
                () =>
                {
                    int knn = 1;
                    double PredictionErrSum = 0.0;
                    foreach (Vector TestedVector in TestVectors)
                    {
                        Dictionary<Vector, double> Closest = new Dictionary<Vector, double>();    //Vector- center of cluster and the distance to curren tested vector
                        for (int i = 0; i < k1.Centroids.Count; i++)    //loop for finding a k nearest neighbours for current tested Vector
                        {
                            if (k1.HowManyInGroupX(i) > 0)
                            {
                                if (i < knn) Closest.Add(k1.Centroids[i], EuclideanDistance(TestedVector, k1.Centroids[i]));
                                else
                                {
                                    double Distance = EuclideanDistance(TestedVector, k1.Centroids[i]);
                                    KeyValuePair<Vector, double> max = new KeyValuePair<Vector, double>(TestedVector, Distance);
                                    foreach (KeyValuePair<Vector, double> kvp in Closest)
                                    {
                                        if (kvp.Value > max.Value)
                                            max = kvp;
                                    }
                                    if (max.Value > Distance)
                                    {
                                        Closest.Remove(max.Key);
                                        Closest.Add(k1.Centroids[i], Distance);
                                    }
                                    /*System.Collections.IDictionaryEnumerator ClosestEnumerator = Closest.GetEnumerator();
                                    int k = 0, j = 0;
                                    KeyValuePair<Vector, double> max = new KeyValuePair<Vector, double>();
                                    while (ClosestEnumerator.MoveNext())
                                    {
                                        if (j == 0)
                                           max = new KeyValuePair<Vector, double>((Vector)ClosestEnumerator.Key, (double)ClosestEnumerator.Value);
                                        else
                                        if (((double)ClosestEnumerator.Value) > max.Value)
                                        {
                                            max = new KeyValuePair<Vector, double>((Vector)ClosestEnumerator.Key, (double)ClosestEnumerator.Value);
                                            k++;
                                        }
                                        j++;
                                    }
                                    if (k > 0)
                                    {
                                        Closest.Remove(max.Key);
                                        Closest.Add(TestedVector, Distance);
                                    }*/
                                }
                            }
                        }
                        //and now predicting YValue, also calculating the difference between predicted and real YValue
                        double PredictedY = 0.0;
                        System.Collections.IDictionaryEnumerator en = Closest.GetEnumerator();
                        while (en.MoveNext())
                        {
                            Vector V1 = (Vector)en.Key;
                            PredictedY += V1.YValue;
                        }
                        PredictedY /= Closest.Count;
                        PredictionErrSum += Math.Pow((PredictedY - TestedVector.YValue), 2); //błąd przybliżenia jako średni błąd kwadratowy
                        //SW.WriteLine("\t\tY: " + TestedVector.YValue + "\tPredictedY: " + PredictedY);
                    }
                    Console.WriteLine("Blad przewidywan dla " + iii + " clustrow(knn=" + knn + "):" + PredictionErrSum);
                    //SW.WriteLine("Blad przewidywan dla " + iii + " clustrow: " + (Math.Sqrt(PredictionErrSum / TestVectors.Count)));
                    Console.WriteLine("Blad przewidywan dla " + iii + " clustrow(knn=" + knn + "):" + (Math.Sqrt(PredictionErrSum) / TestVectors.Count));
                    DateTime AfterOnePass = DateTime.Now;
                    TimeSpan TS1 = AfterOnePass - PreLoopTime;
                    Console.WriteLine(TS1.Seconds.ToString() + "s " + TS1.Milliseconds.ToString() + "ms");

                },
                () =>   //same but with other "k"
                {

                    int knn = 7;
                    double PredictionErrSum = 0.0;
                    foreach (Vector TestedVector in TestVectors)
                    {
                        Dictionary<Vector, double> Closest = new Dictionary<Vector, double>();    //wektor- centroid i przyporządkowana mu odległość
                        for (int i = 0; i < k1.Centroids.Count; i++)    //znajdujemy k-NN dla danego testowanego wektora
                        {
                            if (k1.HowManyInGroupX(i) > 0)
                            {
                                if (i < knn) Closest.Add(k1.Centroids[i], EuclideanDistance(TestedVector, k1.Centroids[i]));
                                else
                                {
                                    double Distance = EuclideanDistance(TestedVector, k1.Centroids[i]);
                                    KeyValuePair<Vector, double> max = new KeyValuePair<Vector, double>(TestedVector, Distance);
                                    foreach (KeyValuePair<Vector, double> kvp in Closest)
                                    {
                                        if (kvp.Value > max.Value)
                                            max = kvp;
                                    }
                                    if (max.Value > Distance)
                                    {
                                        Closest.Remove(max.Key);
                                        Closest.Add(k1.Centroids[i], Distance);
                                    }
                                    /*System.Collections.IDictionaryEnumerator ClosestEnumerator = Closest.GetEnumerator();
                                    int k = 0, j = 0;
                                    KeyValuePair<Vector, double> max = new KeyValuePair<Vector, double>();
                                    while (ClosestEnumerator.MoveNext())
                                    {
                                        if (j == 0)
                                           max = new KeyValuePair<Vector, double>((Vector)ClosestEnumerator.Key, (double)ClosestEnumerator.Value);
                                        else
                                        if (((double)ClosestEnumerator.Value) > max.Value)
                                        {
                                            max = new KeyValuePair<Vector, double>((Vector)ClosestEnumerator.Key, (double)ClosestEnumerator.Value);
                                            k++;
                                        }
                                        j++;
                                    }
                                    if (k > 0)
                                    {
                                        Closest.Remove(max.Key);
                                        Closest.Add(TestedVector, Distance);
                                    }*/
                                }
                            }
                        }
                        double PredictedY = 0.0;
                        System.Collections.IDictionaryEnumerator en = Closest.GetEnumerator();
                        while (en.MoveNext())
                        {
                            Vector V1 = (Vector)en.Key;
                            PredictedY += V1.YValue;
                        }
                        PredictedY /= Closest.Count;
                        /**/
                        SW.Write(TestedVector.YValue + "\t" + PredictedY + "\t\t" + Math.Abs(PredictedY - TestedVector.YValue)); SW.WriteLine();
                        PredictionErrSum += Math.Pow((PredictedY - TestedVector.YValue), 2); //błąd przybliżenia jako średni błąd kwadratowy
                        //SW.WriteLine("\t\tY: " + TestedVector.YValue + "\tPredictedY: " + PredictedY);
                    }
                    Console.WriteLine("Blad przewidywan dla " + iii + " clustrow(knn=" + knn + "):" + PredictionErrSum);
                    //SW.WriteLine("Blad przewidywan dla " + iii + " clustrow: " + (Math.Sqrt(PredictionErrSum / TestVectors.Count)));
                    Console.WriteLine("Blad przewidywan dla " + iii + " clustrow(knn=" + knn + "):" + (Math.Sqrt(PredictionErrSum) / TestVectors.Count));
                    DateTime AfterOnePass = DateTime.Now;
                    TimeSpan TS1 = AfterOnePass - PreLoopTime;
                    Console.WriteLine(TS1.Seconds.ToString() + "s " + TS1.Milliseconds.ToString() + "ms");

                }
                );
            }
            SW.Close();
        }

        public void KnnAlgorithm(int k) //k-NN algorithm as another method
        {
            int knn = k;
            double PredictionErrSum = 0.0;
            DateTime PreLoopTime = DateTime.Now;
            foreach (Vector TestedVector in TestVectors)
            {
                Dictionary<Vector, double> Closest = new Dictionary<Vector, double>();
                for (int i = 0; i < TrnVectors.Count; i++)
                {
                    if (i < knn) Closest.Add(TrnVectors[i], EuclideanDistance(TestedVector, TrnVectors[i]));
                    else
                    {
                        double Distance = EuclideanDistance(TestedVector, TrnVectors[i]);
                        KeyValuePair<Vector, double> max = new KeyValuePair<Vector, double>(TestedVector, Distance);
                        foreach (KeyValuePair<Vector, double> kvp in Closest)
                        {
                            if (kvp.Value > max.Value)
                                max = kvp;
                        }
                        if (max.Value > Distance)
                        {
                            Closest.Remove(max.Key);
                            Closest.Add(TrnVectors[i], Distance);
                        }
                    }
                }
                double PredictedY = 0.0;
                System.Collections.IDictionaryEnumerator en = Closest.GetEnumerator();
                while (en.MoveNext())
                {
                    Vector V1 = (Vector)en.Key;
                    PredictedY += V1.YValue;
                }
                PredictedY /= Closest.Count;
                PredictionErrSum += Math.Pow((PredictedY - TestedVector.YValue), 2); //błąd przybliżenia jako średni błąd kwadratowy
            }
            Console.WriteLine("Blad przewidywan (k=" + knn + "): " + PredictionErrSum + "\t\tMSE: " + (Math.Sqrt(PredictionErrSum) / TestVectors.Count));
            DateTime AfterOnePass = DateTime.Now;
            TimeSpan TS1 = AfterOnePass - PreLoopTime;
            Console.WriteLine(TS1.Seconds.ToString() + "s " + TS1.Milliseconds.ToString() + "ms");
        }

        public void EnnAlgorithm()  //the E-NN algorithm, not sure yet if it works properly
        {
            List<Vector> VectorList = this.TrnVectors;
            double MaxYValue = VectorList[0].YValue, MinYValue = VectorList[0].YValue, AvgAbsYValue = Math.Abs(VectorList[1].YValue);
            for (int i = 1; i < VectorList.Count; i++)
            {
                if (VectorList[i].YValue > MaxYValue) MaxYValue = VectorList[i].YValue;
                if (VectorList[i].YValue < MinYValue) MinYValue = VectorList[i].YValue;
                AvgAbsYValue += Math.Abs(VectorList[i].YValue);
            }
            AvgAbsYValue /= VectorList.Count;
            //Console.WriteLine(MaxYValue);
            //Console.WriteLine(MinYValue);
            double YValueSpan = Math.Abs(MaxYValue - MinYValue);
            //Console.WriteLine(YValueSpan);
            //Console.WriteLine(AvgAbsYValue);
            int knn = 3, Deleted = 0;
            double Precission = AvgAbsYValue * 0.2;
            Precission = 0.3;   //na razie tak

            for (int k = 0; k < VectorList.Count; k++)
            {
                Vector V = VectorList[k];
                Dictionary<Vector, double> Closest = new Dictionary<Vector, double>();
                for (int i = 0; i < VectorList.Count; i++)
                {
                    if (VectorList[i] == V) continue;
                    if (Closest.Count < knn) Closest.Add(VectorList[i], EuclideanDistance(V, VectorList[i]));
                    else
                    {
                        double Distance = EuclideanDistance(V, VectorList[i]);
                        KeyValuePair<Vector, double> max = new KeyValuePair<Vector, double>(V, Distance);
                        foreach (KeyValuePair<Vector, double> kvp in Closest)
                        {
                            if (kvp.Value > max.Value)
                                max = kvp;
                        }
                        if (max.Value > Distance)
                        {
                            Closest.Remove(max.Key);
                            Closest.Add(VectorList[i], Distance);
                        }
                    }
                }
                bool DoRemove = true;
                double AvgClosestValue = 0.0;
                double[] ClosestYValue = new double[Closest.Count];
                System.Collections.IDictionaryEnumerator ClosestEnumerator = Closest.GetEnumerator();
                int s = 0;
                while (ClosestEnumerator.MoveNext())
                {
                    Vector CurrentClosest = (Vector)ClosestEnumerator.Key;
                    AvgClosestValue += CurrentClosest.YValue;
                    ClosestYValue[s] = CurrentClosest.YValue;
                    s++;
                }
                AvgClosestValue /= Closest.Count;
                if (Math.Abs(V.YValue - AvgClosestValue) < Precission)
                    DoRemove = false;
                if (DoRemove)
                {
                    //Console.WriteLine(ClosestYValue[0]+"\t"+ClosestYValue[1]+"\t"+ClosestYValue[2]+"\t\t"+V.YValue);
                    VectorList.Remove(V);
                    Deleted++;
                }
            }
            this.TrnVectors = VectorList;
            Console.WriteLine("Usunieto przez enn: " + Deleted);
            Console.WriteLine("Zostalo: " + VectorList.Count);
        }

        public void CnnAlgorithm()  //the C-NN algorithm, not sure yet if it works properly
        {
            List<Vector> VectorList = this.TrnVectors;
            double MaxYValue = VectorList[0].YValue, MinYValue = VectorList[0].YValue, AvgAbsYValue = Math.Abs(VectorList[1].YValue);
            for (int i = 1; i < VectorList.Count; i++)
            {
                if (VectorList[i].YValue > MaxYValue) MaxYValue = VectorList[i].YValue;
                if (VectorList[i].YValue < MinYValue) MinYValue = VectorList[i].YValue;
                AvgAbsYValue += Math.Abs(VectorList[i].YValue);
            }
            AvgAbsYValue /= VectorList.Count;
            //Console.WriteLine(MaxYValue);
            //Console.WriteLine(MinYValue);
            double YValueSpan = Math.Abs(MaxYValue - MinYValue);
            //Console.WriteLine(YValueSpan);
            //Console.WriteLine(AvgAbsYValue);
            int knn = 3, Deleted = 0;
            double Precission = AvgAbsYValue * 0.2;
            Precission = 0.2;   //na razie tak

            for (int k = 0; k < VectorList.Count; k++)
            {
                Vector V = VectorList[k];
                Dictionary<Vector, double> Closest = new Dictionary<Vector, double>();
                for (int i = 0; i < VectorList.Count; i++)
                {
                    if (VectorList[i] == V) continue;
                    if (Closest.Count < knn) Closest.Add(VectorList[i], EuclideanDistance(V, VectorList[i]));
                    else
                    {
                        double Distance = EuclideanDistance(V, VectorList[i]);
                        KeyValuePair<Vector, double> max = new KeyValuePair<Vector, double>(V, Distance);
                        foreach (KeyValuePair<Vector, double> kvp in Closest)
                        {
                            if (kvp.Value > max.Value)
                                max = kvp;
                        }
                        if (max.Value > Distance)
                        {
                            Closest.Remove(max.Key);
                            Closest.Add(VectorList[i], Distance);
                        }
                    }
                }
                bool DoRemove = false;
                double AvgClosestValue = 0.0;
                double[] ClosestYValue = new double[Closest.Count];
                System.Collections.IDictionaryEnumerator ClosestEnumerator = Closest.GetEnumerator();
                int s = 0;
                while (ClosestEnumerator.MoveNext())
                {
                    Vector CurrentClosest = (Vector)ClosestEnumerator.Key;
                    AvgClosestValue += CurrentClosest.YValue;
                    ClosestYValue[s] = CurrentClosest.YValue;
                    s++;
                }
                AvgClosestValue /= Closest.Count;
                if (Math.Abs(V.YValue - AvgClosestValue) < Precission)
                    DoRemove = true;
                if (DoRemove)
                {
                    Console.WriteLine(ClosestYValue[0] + "\t" + ClosestYValue[1] + "\t" + ClosestYValue[2] + "\t\t" + V.YValue);
                    VectorList.Remove(V);
                    Deleted++;
                }
            }
            this.TrnVectors = VectorList;
            Console.WriteLine("Usunieto przez cnn: " + Deleted);
            Console.WriteLine("Zostalo: " + VectorList.Count);
        }
    }
}